package com.opennews.openplatform.service;

import com.opennews.openplatform.entity.tables.records.AppReleaseLogRecord;
import com.opennews.openplatform.jooq.DSLContextWrapper;
import com.opennews.openplatform.jooq.RecordHelper;
import lombok.RequiredArgsConstructor;
import org.jooq.DSLContext;
import org.jooq.impl.DSL;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;

import static com.opennews.openplatform.entity.Tables.APP_RELEASE_LOG;

@RequiredArgsConstructor
@Service
public class AppReleaseLogService {
    private final DSLContext dslContext;
    private final DSLContextWrapper dslContextWrapper;
    private final Environment env;

    /**
     * Prepares the initial data for AppReleaseLog.
     */
    public void init() {
        if (dslContextWrapper.count(APP_RELEASE_LOG) == 0) {
            AppReleaseLogRecord record = dslContext.newRecord(APP_RELEASE_LOG);
            RecordHelper.setCommonFields(record);
            record.setReleaseDate(LocalDateTime.of(2019, 12, 1, 0, 0));
            record.setAppVersion("1.0.0");
            record.setChangeLog("初始版本");
            record.setApkFilename("family-expense.apk");
            record.setAppStoreUrl("https://");
            record.insert();
        }
    }

    /**
     * Queries the app release details by its version.
     *
     * @param appVersion: The string of the app version.
     * @return The map instance which contains keys below.
     * releaseDate: Date when the app released.
     * appVersion: String of the app version.
     * apkFilename: String of the apk filename.
     * apkUrl: String of apk url for android.
     * isMandatory: If this is a mandatory update.
     */
    public Map<String, Object> queryByAppVersion(String appVersion) {
        Map<String, Object> result = dslContext.select(
                        APP_RELEASE_LOG.RELEASE_DATE.as("releaseDate"),
                        APP_RELEASE_LOG.APP_VERSION.as("appVersion"),
                        APP_RELEASE_LOG.APK_FILENAME.as("apkFilename"),
                        APP_RELEASE_LOG.IS_MANDATORY.as("isMandatory")
                )
                .from(APP_RELEASE_LOG)
                .where(APP_RELEASE_LOG.APP_VERSION.eq(appVersion))
                .fetchOneMap();

        if (result != null) {
            result.put("apkUrl", String.format("%s/%s/${result.apkFilename}",
                    env.getProperty("resource.httpBaseUrl"),
                    env.getProperty("resource.apkDirectoryName"),
                    result.get("apkFilename")));
        }

        return result;
    }

    /**
     * Queries the latest version which released after the provided releaseDate.
     *
     * @param appVersion: The string of the app version.
     * @return The map instance which contains keys below.
     * releaseDate: Date when the app released.
     * appVersion: String of the app version.
     * changeLog: String of the release change log.
     * apkFilename: String of the apk filename.
     * apkUrl: String of apk url for android.
     * appStoreUrl: String of app store url for ios.
     * isMandatory: If this is a mandatory update.
     */
    public Map<String, Object> queryLatestVersion(String appVersion) {
        // Different database environment causes different date type value.
        // For example, the value in database is '2019-12-31 00:00:00' but the retrieved value
        // might be '2019-12-31 14:00:00'. It might be caused by time zone setting.
        // That's why using sub-query in where instead of using the input releaseDate as parameter.
        var releaseDateQuery = dslContext.select(APP_RELEASE_LOG.RELEASE_DATE)
                .from(APP_RELEASE_LOG)
                .where(APP_RELEASE_LOG.APP_VERSION.eq(appVersion));

        var isMandatoryQuery = dslContext.select(DSL.count(APP_RELEASE_LOG.ID))
                .from(APP_RELEASE_LOG)
                .where(
                        APP_RELEASE_LOG.RELEASE_DATE.gt(releaseDateQuery)
                                .and(APP_RELEASE_LOG.IS_MANDATORY.isTrue())
                )
                .asField();

        Map<String, Object> result = dslContext.select(
                        APP_RELEASE_LOG.RELEASE_DATE.as("releaseDate"),
                        APP_RELEASE_LOG.APP_VERSION.as("appVersion"),
                        APP_RELEASE_LOG.CHANGE_LOG.as("changeLog"),
                        APP_RELEASE_LOG.APK_FILENAME.as("apkFilename"),
                        APP_RELEASE_LOG.APP_STORE_URL.as("appStoreUrl"),
                        isMandatoryQuery.gt(0).as("isMandatory")
                )
                .from(APP_RELEASE_LOG)
                .where(APP_RELEASE_LOG.RELEASE_DATE.gt(releaseDateQuery))
                .orderBy(APP_RELEASE_LOG.RELEASE_DATE.desc())
                .limit(1)
                .fetchOneMap();

        if (result != null) {
            result.put("apkUrl", String.format("%s/%s/%s",
                    env.getProperty("resource.httpBaseUrl"),
                    env.getProperty("resource.apkDirectoryName"),
                    result.get("apkFilename")));
        }

        return result;
    }

    /**
     * Lists change log info.
     *
     * @param pageSize:  How many records is return in one trip.
     * @param pageIndex: Where to take rows.
     * @return The list of map instance which contains keys below.
     * id: String of the app release log id.
     * releaseDate: Date of this version released.
     * appVersion: The string of this app version.
     * changeLog: The string of this change log info.
     * isMandatory: If this is a mandatory update.
     */
    public List<Map<String, Object>> listChangeLog(Integer pageSize, Integer pageIndex) {
        return dslContext.select(
                        APP_RELEASE_LOG.ID.as("id"),
                        APP_RELEASE_LOG.RELEASE_DATE.as("releaseDate"),
                        APP_RELEASE_LOG.APP_VERSION.as("appVersion"),
                        APP_RELEASE_LOG.CHANGE_LOG.as("changeLog"),
                        APP_RELEASE_LOG.IS_MANDATORY.as("isMandatory")
                )
                .from(APP_RELEASE_LOG)
                .where()
                .orderBy(APP_RELEASE_LOG.RELEASE_DATE.desc())
                .limit(pageSize)
                .offset(pageIndex * pageSize)
                .fetchMaps();
    }
}
